package raw_query

import (
	"context"
	"database/sql"
	"reflect"
	errors2 "server/framework/orm/errors"
	"server/framework/orm/session/db"
	value2 "server/framework/orm/session/value"
	sql2 "server/framework/orm/sql"
	"server/framework/orm/utils"
)

type RawQuerior[T any] struct {
	sess  db.Session
	query *sql2.Query
	db.Core
}

func (r *RawQuerior[T]) Exec(ctx context.Context) *sql2.QueryResult {
	res, err := r.sess.ExecContext(ctx, r.query.SQL, r.query.Args...)
	return &sql2.QueryResult{
		Res: res,
		Err: err,
	}
}

func (r *RawQuerior[T]) Get(ctx context.Context, val interface{}) *sql2.QueryResult {
	rows, err := r.sess.QueryContext(ctx, r.query.SQL, r.query.Args...)
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	defer rows.Close()

	if !rows.Next() {
		return &sql2.QueryResult{
			Err: errors2.ErrNoRows,
		}
	}

	var value value2.Value
	model := r.R.Get(val)
	value, err = r.ValueCreator(val, model)
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	err = value.SetColumns(rows, r.Dialect.Quote())
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	return nil
}

func (r *RawQuerior[T]) GetMutli(ctx context.Context, vals interface{}) *sql2.QueryResult {
	elem, err := utils.ValidateValsType(vals)
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	var rows *sql.Rows
	rows, err = r.sess.QueryContext(ctx, r.query.SQL, r.query.Args...)
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	defer rows.Close()

	valInstance := reflect.New(elem).Interface() // 取值
	model := r.R.Get(valInstance)

	var values *value2.ReflectValues
	values, err = value2.NewReflectValues(vals, model, value2.WithValueCreator(r.ValueCreator))
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	err = values.SetValues(rows, r.Dialect.Quote())
	if err != nil {
		return &sql2.QueryResult{
			Err: err,
		}
	}
	return nil
}

func RawQuery[T any](sess db.Session, query *sql2.Query) *RawQuerior[T] {
	return &RawQuerior[T]{
		query: query,
		sess:  sess,
		Core:  sess.GetCore(),
	}
}

var _ sql2.Querior[any] = (*RawQuerior[any])(nil)
var _ sql2.Executor[any] = (*RawQuerior[any])(nil)
